/*
 * Copyright 2010,2011,2012 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.schnipsel.swt.editor;

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import de.mnit.basis.daten.SucheText;
import de.mnit.basis.daten.konstant.QUALITAET;
import de.mnit.basis.daten.konstant.position.POSITION_V;
import de.mnit.basis.daten.struktur.gruppe.Gruppe2;
import de.mnit.basis.daten.struktur.gruppe.Gruppe3;
import de.mnit.basis.daten.struktur.tabelle.typ.TypTabelle3;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.swt.anw.A_MN_Gruppe;
import de.mnit.basis.swt.dialog.SWT_Fehler;
import de.mnit.basis.swt.element.S_SWT_Komponente;
import de.mnit.basis.swt.element.feld.werkzeug.knopf.SWT_WerkzeugSchalter;
import de.mnit.basis.swt.element.feld.werkzeug.knopf.SWT_WerkzeugTaster;
import de.mnit.basis.swt.element.steuer.rollbar.gruppe.SWT_WerkzeugLeiste;
import de.mnit.basis.swt.element.steuer.rollbar.gruppe.bild.SWT_TextEditor;
import de.mnit.basis.swt.grafik.icon.direkt.ICON;
import de.mnit.basis.swt.konstant.SWT_SCHRIFT;
import de.mnit.basis.swt.konstant.SWT_SCHRIFTSTIL;
import de.mnit.basis.swt.layout.LayoutAbsolut;
import de.mnit.basis.swt.layout.LayoutGitter;
import de.mnit.basis.swt.statisch.FarbManager;
import de.mnit.basis.sys.Ausgabe;
import de.mnit.basis.sys.event.S_Zuhoerer;
import de.mnit.basis.sys.sprache.S;
import de.mnit.schnipsel.db.B_Schnipsel;
import de.mnit.schnipsel.db.CRYPTLEVEL;
import de.mnit.schnipsel.db.DBManager;
import de.mnit.schnipsel.div.ScHilfe;
import de.mnit.schnipsel.swt.dialog.K_ArtikelLink;
import de.mnit.schnipsel.swt.dialog.K_DateiLink;
import de.mnit.schnipsel.swt.dialog.K_WebLink;
import de.mnit.schnipsel.swt.dialog.S_LinkDialog;
import de.mnit.schnipsel.swt.editor.basis.LINKART;
import de.mnit.schnipsel.swt.editor.basis.LinkInfo;
import de.mnit.schnipsel.swt.editor.basis.S_StyleKontrolle;
import de.mnit.schnipsel.swt.editor.basis.TE_STYLE;
import de.mnit.schnipsel.swt.editor.basis.TextEditor;
import de.mnit.schnipsel.zentral.SC_Bilder;
import de.mnit.schnipsel.zentral.SC_Events;



/**
 * @author Michael Nitsche
 * 23.05.2009	Erstellt
 *
 * INFO:
 * the new styles, must be in order and non-overlapping
 */
public class TextEditorGui extends A_MN_Gruppe<TextEditorGui> {

	public static void main(String[] args) {
		QUALITAET.setze(QUALITAET.TEST);
		start(TextEditorGui.class, 600,400);
	}

	private static final S s=new S(TextEditorGui.class);
	private static final String E_GEAENDERT="GEAENDERT";
	private final SWT_WerkzeugLeiste k_leiste=new SWT_WerkzeugLeiste().s1_Flach();
	private final TextEditor k_editor=new TextEditor().s1_Rollbalken_V();
	private SWT_WerkzeugSchalter wsF=null, wsK=null, wsU=null, wsS=null, wsH=null;//, wsD=null;
	private K_EditorAnzeigeAlter k_alter = new K_EditorAnzeigeAlter();
	private SWT_WerkzeugTaster speichern, muell;
	private SWT_WerkzeugSchalter geschuetzt;
	private SWT_WerkzeugSchalter ablauf;
	private EditorPopUp menu=new EditorPopUp(k_editor);
	private StyleRange[] ohneSuche=null;


	public void sLeer() {
		t.k_editor.sLeer();
	}

	public void sSpeichern(boolean moeglich) {
		if(speichern!=null)
			speichern.sAktiviert(moeglich);
	}

	public void setze(B_Schnipsel s, String suche) {
		CRYPTLEVEL crypt=CRYPTLEVEL.gib(s.CRYPT_INHALT.g());
		String text=s.TEXT.g();

		this.muell.sAktiviert(text!=null);
		Fehler.objekt.wenn_Null(crypt);
		t.k_editor.sTextUndFormat(text, s.FORMAT.g());
		this.k_alter.setze(s.ERSTELLT.g(), s.GEAENDERT.g(), text==null ? 0 : text.length());

		boolean cwert= crypt==CRYPTLEVEL.INHALT;
		this.geschuetzt.sAusgewaehlt(cwert);
		this.ablauf.sAusgewaehlt(s.ABLAUF.g()!=null);
		iSucheHervorheben(s.TEXT.g(), suche);
	}

	public void sWordWrap() {
		t.k_editor.sWordWrap();
	}

	public void sSchrift(SWT_SCHRIFT schrift, int groesse, SWT_SCHRIFTSTIL... stile) {
		t.k_editor.sSchrift(schrift, groesse, stile);
	}

	public Gruppe2<String, String> gTextUndFormat() {
		iAbschlussFormatierung();
		return t.k_editor.gTextUndFormat();
	}

	public SWT_TextEditor gEditor() {
		return t.k_editor.gEditor();
	}

	public CRYPTLEVEL gGeschuetzt() {
		return this.geschuetzt.gAusgewaehlt() ? CRYPTLEVEL.INHALT : CRYPTLEVEL.OFFEN;
	}

	public void sGeschuetzt() {
		this.geschuetzt.sAusgewaehlt(true);
	}

	/** @deprecated FIXME Nötig??? **/
	public boolean gAblauf() {
		return this.ablauf.gAusgewaehlt();
	}

	public void eTaste(KeyListener k) {
		t.k_editor.eTaste(k);
	}

	public void eGeaendert(S_Zuhoerer<Object> z) {
		t.k_editor.eGeaendert(z);
		t.zuhoererPlus(E_GEAENDERT, z);
	}

	// ERZEUGEN

	protected S_SWT_Komponente<?,? extends Composite> layout() {
		S_SWT_Komponente<?,?> ro=LayoutGitter.neu()
			.sSpaltenAnzahl(2)
			.sKomponente(this.k_leiste)
				.sFlexibel()
				.fertig()
			.sKomponente(k_alter)
				.sBreite(100)
				.sPositionV(POSITION_V.MITTE)
				.fertig();
		return LayoutAbsolut.neu()
			.sKomponente(ro).sLORU(0, 0, 0, null).fertig()
			.sKomponente(t.k_editor).sLORU(0, ro, 0, 0).fertig();
	}

//	protected void initStyle() {
//		this.k_editor.sText(ZEICHEN.DEUTSCH); //Zum testen
//	}

	protected void initStyle() {
//		this.k_editor.gEditor().swt().getVerticalBar().setVisible(true);
	}

	protected void initEvents() {
		final int iconGroesse=16;
		this.menu.init();

		this.speichern=t.k_leiste.nFeldBild(ICON._16.AKTION_SPEICHERN, s.t("Speichern"));
		this.speichern.eAuswahl(new Listener() {
			public void handleEvent(Event event) {
				SC_Events.schnipselSpeichern();
			}
		});
		this.speichern.sAktiviert(false);

		this.muell=t.k_leiste.nFeldBild(SC_Bilder.gIcon_16_Baum_Muell, s.t("Eintrag in den Papierkorb"));
		this.muell.eAuswahl(new Listener() {
			public void handleEvent(Event event) {
				SC_Events.schnipselMinus();
			}
		});
		this.muell.sAktiviert(false);

		t.k_leiste.nFeldTrenner();

		t.k_leiste.nFeldBild(ICON.gib(iconGroesse, "deb_tango", "printer.png"), s.t("Drucken")).eAuswahl(new Listener() {
			public void handleEvent(Event event) {
				SC_Events.schnipselDrucken();
			}
		});

		t.k_leiste.nFeldBild(ICON._16.PFEIL_RUNTER, s.t("Exportieren")).eAuswahl(new Listener() {
			public void handleEvent(Event e) {
				SC_Events.export(t.k_editor);
			}
		});

		t.k_leiste.nFeldTrenner();

		this.geschuetzt=this.k_leiste.nFeldSchalter(SC_Bilder.gIcon_16_Sicherheit, s.t("Inhalt ungeschützt"), SC_Bilder.gIcon_16_Sicherheit, s.t("Inhalt verschlüsselt aber nicht durchsuchbar!"));

		this.ablauf=t.k_leiste.nFeldSchalter(ICON._16.OBJ_ZEIT, s.t("Ablaufdatum"), ICON._16.OBJ_ZEIT, s.t("Ablaufdatum"));

		t.k_leiste.nFeldTrenner();

		this.k_leiste.nFeldBild(ICON.gib(iconGroesse, "deb_tango", "edit-undo.png"), s.t("Rückgängig")).eAuswahl(new Listener(){
			public void handleEvent(Event event) {
				t.k_editor.zurueck();
			}
		});
		this.k_leiste.nFeldBild(ICON.gib(iconGroesse, "deb_tango", "edit-redo.png"), s.t("Wiederherstellen")).eAuswahl(new Listener(){
			public void handleEvent(Event event) {
				t.k_editor.wiederherstellen();
			}
		});

		t.k_leiste.nFeldTrenner();

		wsH=this.k_leiste.nFeldSchalter(ICON.gib("/jar/icon/ueberschrift_16.png"), s.t("Überschrift"));
		registriere(TE_STYLE.UEBERSCHRIFT, wsH);

		wsF=this.k_leiste.nFeldSchalter(ICON.gib(iconGroesse, "deb_tango", "format-text-bold.png"), s.t("Fett"), ICON.gib(iconGroesse, "deb_tango", "format-text-bold.png"), s.t("Fett"));
		registriere(TE_STYLE.FETT, wsF);

		wsK=this.k_leiste.nFeldSchalter(ICON.gib(iconGroesse, "deb_tango", "format-text-italic.png"), s.t("Kursiv"), ICON.gib(iconGroesse, "deb_tango", "format-text-italic.png"), s.t("Kursiv"));
		registriere(TE_STYLE.KURSIV, wsK);

		wsU=this.k_leiste.nFeldSchalter(ICON.gib(iconGroesse, "deb_tango", "format-text-underline.png"), s.t("Unterstrichen"), ICON.gib(iconGroesse, "deb_tango", "format-text-underline.png"), s.t("Unterstrichen"));
		registriere(TE_STYLE.UNTERSTRICHEN, wsU);

//		wsD=this.k_leiste.nFeldSchalter(ICON.gib(iconGroesse, "deb_tango", "format-text-underline.png"), s.t("Doppelt unterstrichen"), ICON.gib(iconGroesse, "deb_tango", "format-text-underline.png"), s.t("Doppelt unterstrichen"));
//		registriere(TE_STYLE.DOPPELTUNTER, wsD);

		wsS=this.k_leiste.nFeldSchalter(ICON.gib(iconGroesse, "deb_tango", "format-text-strikethrough.png"), s.t("Durchgestrichen"), ICON.gib(iconGroesse, "deb_tango", "format-text-strikethrough.png"), s.t("Durchgestrichen"));
		registriere(TE_STYLE.DURCHGESTRICHEN, wsS);

//		this.k_leiste.nFeldTrenner();
//		t.k_leiste.nFeldBild(ICON.gib(24, "deb_tango", "text-html.png"), "HTML anzeigen").eAuswahl(new Listener(){
//			public void handleEvent(Event event) {
//				Ausgabe.text(gHTML());
//			}
//		});

		this.k_leiste.nFeldTrenner();

		final SWT_TextEditor editor=t.k_editor.gEditor();
		final StyledText styledText=editor.swt();

		this.k_leiste.nFeldBild(ICON.gib("/jar/icon/link_web.png"), s.t("HTML Link")).eAuswahl(new Listener(){
			public void handleEvent(Event event) {
				iLinkNeu(LINKART.WEB, editor);
			}
		});

		this.k_leiste.nFeldBild(ICON.gib("/jar/icon/link_datei.png"), s.t("Datei Link")).eAuswahl(new Listener(){
			public void handleEvent(Event event) {
				iLinkNeu(LINKART.FILE, editor);
			}
		});

		this.k_leiste.nFeldBild(ICON.gib("/jar/icon/link_intern.png"), s.t("Artikel Link")).eAuswahl(new Listener(){
			public void handleEvent(Event event) {
				iLinkNeu(LINKART.ID, editor);
			}
		});

		/** Link wurde angeklickt **/
		styledText.addMouseListener(new MouseListener() {
			public void mouseDoubleClick(MouseEvent e) {}
			public void mouseDown(MouseEvent e) {}
			public void mouseUp(MouseEvent e) {
				if(e.button!=1 || styledText.getSelectionCount()>0)
					return;

				StyleRange sr=null;
				try {
					int offset=styledText.getOffsetAtLocation(new Point(e.x,e.y));
					sr=styledText.getStyleRangeAtOffset(offset);
				}
				catch(IllegalArgumentException e1) {
					return;
				}
				if(sr==null)
					return;
				if(sr.underline && sr.underlineStyle==SWT.UNDERLINE_LINK)
					try {
						LinkInfo linkinfo=(LinkInfo)sr.data;
						LINKART art=linkinfo.art;
						Object daten=linkinfo.daten;
						Ausgabe.debug(linkinfo, art, daten);

						if(art==LINKART.WEB)
							Desktop.getDesktop().browse(new URI(""+daten));
						if(art==LINKART.FILE)
							Desktop.getDesktop().open(new File(""+daten));
						if(art==LINKART.ID)
							SC_Events.zeigArtikel(Integer.parseInt(""+daten), true);
					} catch (IOException e1) {
						Fehler.zeig(e1, false);
					} catch (URISyntaxException e1) {
						Fehler.zeig(e1, false);
					}
			}
		});

		t.geschuetzt.eAuswahl(new Listener() {
			public void handleEvent(Event e) {
				t.zuhoererStart(E_GEAENDERT);
			}
		});
		t.ablauf.eAuswahl(new Listener() {
			public void handleEvent(Event e) {
				boolean aktuell=!t.ablauf.gAusgewaehlt(); //Invertiert, da beim Anklicken geändert!
				Boolean auswahl=SC_Events.ablaufDatum();
				t.ablauf.sAusgewaehlt(auswahl!=null);
				if(aktuell==(auswahl==null) || (auswahl!=null && auswahl==true))
					t.zuhoererStart(E_GEAENDERT);
			}
		});

		/** Link-ToolTip **/
		styledText.addListener(SWT.MouseHover, new Listener() {
			public void handleEvent(final Event event) {
				StyleRange sr=t.k_editor.gEditor().gOffsetStyle(event.x, event.y);
				String text=null;
				if(sr!=null && sr.data!=null && sr.data.getClass().equals(LinkInfo.class)) {
					LinkInfo link=(LinkInfo)sr.data;
					if(link.art==LINKART.ID)
						try {
							text=DBManager.schnipsel().gEintragB(Integer.parseInt(""+link.daten)).TITEL.g();
						}
						catch(Exception e) {
							SWT_Fehler.zeig(e, false);
							text=null;
						}
					else
						text=""+link.daten; //Evtl. noch differenzieren
				}
				t.k_editor.gEditor().sToolTip(text==null ? "" : ""+text); //>> TODO Funktion in TextEditor
			}
		});

		// ToolTip ausblenden, sobald getippt wird
		styledText.addListener(SWT.KeyDown, new Listener() {
			public void handleEvent(Event arg0) {
				t.k_editor.gEditor().sToolTip("");
			}
		});

		// Suchemarkierungen ausblenden, sobald editiert wird!
		styledText.addFocusListener(new FocusListener(){
			public void focusGained(FocusEvent arg0) {
				if(ohneSuche!=null) {
					t.k_editor.gEditor().swt().setStyleRanges(ohneSuche);
					ohneSuche=null;
				}
			}
			public void focusLost(FocusEvent arg0) {}
		});
	}

	// INTERN

	private void iLinkNeu(LINKART art, SWT_TextEditor editor) {
		String auswahl=editor.swt().getSelectionText();
		if(auswahl==null)
			auswahl="";
		S_LinkDialog dialog=
			art==LINKART.WEB ? new K_WebLink() :
			art==LINKART.FILE ? new K_DateiLink() : new K_ArtikelLink();
		if(auswahl.length()>0)
			dialog.setze(new LinkInfo(auswahl,LINKART.WEB, null));
		LinkInfo info=dialog.gErgebnis(f());
		if(info==null)
			return;

		if(!auswahl.equals(info.titel)) {
			Point cursor=editor.einfuegen(info.titel);
			iAlsLink(editor.swt(), info, cursor.x, cursor.y-cursor.x ,art);
		}
	}

	private void registriere(TE_STYLE style, final SWT_WerkzeugSchalter schalter) {
		S_StyleKontrolle sk=new S_StyleKontrolle() {
			public boolean gib() {
				return schalter.gAusgewaehlt();
			}
			public void setze(boolean s) {
				schalter.sAusgewaehlt(s);
			}
			public void sEvent(Listener l) {
				schalter.eAuswahl(l);
			}
		};
		t.k_editor.registriereKontrolle(style, sk);
	}

	/**
	 * Macht einen bestimmten Text zu einem Link
	 * Es wird angenommen, dass der Text bereits eingefügt ist!
	 */
	private void iAlsLink(final StyledText styledText, final LinkInfo info, int start, int laenge, LINKART art) {
		StyleRange styleRange=new StyleRange();
		Ausgabe.debug(start, laenge);//, lenGesamt);
		styleRange.start=start;
		styleRange.length=laenge;
		LINKART.formatLink(styleRange, art, info);
		styledText.replaceStyleRanges(styleRange.start, styleRange.length, new StyleRange[] {styleRange});
	}

	private void iAbschlussFormatierung() {
		//Links erzeugen
		String text=t.k_editor.gEditor().gText();
		TypTabelle3<Integer,Integer,String> tab=SucheText.suche(text, "(?i)(((http|https|ftp)://)|(www\\.))\\S*");
		for(Gruppe3<Integer,Integer,String> g : tab) {
//			Ausgabe.debug(iBereitsLink(t.k_editor.gEditor().swt(), g.g1()));
			if(!iBereitsLink(t.k_editor.gEditor().swt(), g.g1())) {
				String link=g.g3().matches("(?i)^www\\..*") ? "http://"+g.g3() : g.g3();
				LinkInfo info=new LinkInfo(g.g3(), LINKART.WEB, link);
				iAlsLink(t.k_editor.gEditor().swt(), info, g.g1(), g.g2()-g.g1(), LINKART.WEB);
			}
		}
	}

	private boolean iBereitsLink(StyledText styledText, int start) {
		StyleRange range=styledText.getStyleRangeAtOffset(start);
		return range!=null && range.data!=null && range.data.getClass().equals(LinkInfo.class);
	}

	private void iSucheHervorheben(String text, String suche) {
		this.ohneSuche=null; //Wichtig!
		if(suche==null)
			return;

		StyledText st=t.k_editor.gEditor().swt();
		this.ohneSuche=st.getStyleRanges();

		text=text.toLowerCase();
		String[] woerter=ScHilfe.suche(suche.toLowerCase());
		boolean erster=true;
		for(String wort : woerter) {
			Integer start=null;
			while(start==null || (start>=0 && start<text.length())) {
				start=text.indexOf(wort, start==null ? 0 : start+1);
				if(start>=0) {
					StyleRange sr=new StyleRange();
					sr.start=start;
					sr.length=wort.length();
					sr.background=FarbManager.gFarbe(255,239,0);
					st.replaceStyleRanges(start, wort.length(), new StyleRange[]{sr});

					if(erster)
						st.setTopIndex(st.getLineAtOffset(start));
					erster=false;
				}
			}
		}
	}
}
